using System.ComponentModel;

namespace GeneticAlgorithms
{
    /// <summary>
    /// Provides a configuration class for a Genetic Algorithm
    /// </summary>
    public class GAConfigurationBase : IGAConfiguration
    {
        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="GAConfigurationBase"/> class.
        /// </summary>
        public GAConfigurationBase()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="GAConfigurationBase"/> class.
        /// </summary>
        /// <param name="desiredFitness">The desired fitness.</param>
        public GAConfigurationBase(double desiredFitness)
            : this(desiredFitness, null, null, false, null)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="GAConfigurationBase"/> class.
        /// </summary>
        /// <param name="maxGenerations">The max generations to evolve.</param>
        public GAConfigurationBase(int maxGenerations)
            : this(null, maxGenerations, null, false, null)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="GAConfigurationBase"/> class.
        /// </summary>
        /// <param name="desiredFitness">The desired fitness.</param>
        /// <param name="maxGenerations">The max generations to evolve.</param>
        /// <param name="maxMilliseconds">The max milliseconds to let elapse.</param>
        /// <param name="calculateVariance">if set to <c>true</c>, will calculate variance.</param>
        /// <param name="minGenerationVariance">
        /// The min variance between generations. 
        /// Will stop when this value falls below the given thresold.
        /// </param>
        public GAConfigurationBase(
            double? desiredFitness,
            int? maxGenerations,
            int? maxMilliseconds,
            bool calculateVariance,
            double? minGenerationVariance)
        {
            DesiredFitness = desiredFitness;
            MaxGenerations = maxGenerations;
            MaxMilliseconds = maxMilliseconds;
            CalculateVariance = calculateVariance;
            MinGenerationVariance = minGenerationVariance;
        }

        #endregion

        #region Properties and fields

        /// <summary>
        /// Gets or sets the maxValue number of generations allowed.
        /// </summary>
        /// <value>The maxValue number of generations allowed.</value>
        [Category("Stop Conditions")]
        [Description("Determines the maxValue number of generations allowed.")]
        public int? MaxGenerations
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the desired fitness to be attained.
        /// </summary>
        /// <value>The desired fitness to be attained.</value>
        /// <remarks>
        /// If this property is set, the GA will stop when at least
        /// one genome has reached the desired fitness.
        /// </remarks>
        [Category("Stop Conditions")]
        [Description("The (best) fitness value to be reached.")]
        public double? DesiredFitness
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the max milliseconds.
        /// </summary>
        /// <value>The max milliseconds.</value>
        /// <remarks>
        /// If this property is set, the GA will stop after the
        /// given amount of milliseconds has elapsed.
        /// </remarks>
        [Category("Stop Conditions")]
        [Description("Determines the maximum time allowed for evolution.")]
        public long? MaxMilliseconds
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets a value indicating whether the GA should
        /// calculate variance at each generation.
        /// </summary>
        /// <value>
        /// If set to <c>true</c>, the GA will calculate variance
        /// at each evolutional step.
        /// </value>
        /// <remarks>
        /// Since variance calculation can have a performance impact,
        /// this option is off by default.
        /// </remarks>
        [Category("Calculations")]
        [Description("Determines whether the GA should calculate variance among genomes.")]
        public bool CalculateVariance
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the min generation variance.
        /// </summary>
        /// <value>The min generation variance.</value>
        /// <remarks>
        /// If this property is set, the GA will stop when the
        /// generational variance has fallen before this threshold.
        /// </remarks>
        [Category("Calculations")]
        [Description("Indicates the minimum generational variance to continue evolution.")]
        public double? MinGenerationVariance
        {
            get;
            set;
        }

        #endregion

        #region Static members

        private static readonly double? DefaultDesiredFitness = null;
        private static readonly int? DefaultMaxGenerations = 1000;
        private static readonly int? DefaultMaxMilliseconds = 3000;
        private static readonly bool DefaultCalculateVariance = false;
        private static readonly double? DefaultMinGenerationVariance = null;

        private static GAConfigurationBase defaultInstance =
            new GAConfigurationBase(
                DefaultDesiredFitness,
                DefaultMaxGenerations,
                DefaultMaxMilliseconds,
                DefaultCalculateVariance,
                DefaultMinGenerationVariance);

        /// <summary>
        /// Gets the default configuration.
        /// </summary>
        /// <value>The default configuration.</value>
        public static GAConfigurationBase Default
        {
            get { return defaultInstance; }
        }

        #endregion
    }
}
